from symbol_table_entry import SymbolTableEntry
from js_type import *
from symbol_table_exception import StException
from time import time
import re

class SymbolTable ():

	def __init__(self,name,depth=1):
		self.name = name
		self.st = []
		self.currentOffset = 0 #current currentOffset
		self.depth = depth
		self.finished = False

	def getSymbolTableEntries(self):
		return self.st

	def getRaSize(self):
		# if finished, all lexemes and temporals are adjacent
		# and their offset updated
		if self.finished:
			return self.currentOffset
		else:
			return None

	def setDepth(self,depth):
		self.depth = depth

	def getDepth(self):
		return self.depth

	def getName(self):
		return self.name

	def setName(self,name):
		self.name = name

	def insert(self,lexeme,jsType=None):
		if (jsType == None):
			jsType = IntegerType() # by default, integer

		stEntry = SymbolTableEntry(lexeme,self.currentOffset)
		stEntry.setType(jsType) # update offset in entry
		#self.currentOffset = stEntry.getOffset() # finally update current offset
		self.st.append(stEntry)

		return len(self.st)-1 #list index starts with 0

	
	def insertTemporal(self,lexeme,jsType):
		# temporal does not have offset
		# this is set when finished in order to get all lexemes
		# adjacents and then temporal variables
		stEntry = SymbolTableEntry(lexeme,self.currentOffset)
		stEntry.setType(jsType) # update offset in entry
		stEntry.setTemporal()
		self.st.append(stEntry)
		return len(self.st)-1 #list index starts with 0

	def getSymbolTableEntry(self,pos):
		try:
			return self.st[pos]
		except:
			return None

	def setType(self,lexeme,newType):
		pos = self.getPos(lexeme)

		if (pos == None or not isinstance(newType,Type)):
			#print "SymbolTable:%s:No se puede indicar el tipo para: %s"%(self.name,lexeme)
			return None
		else:
			oldType = self.getType(lexeme)
		 	self.st[pos].setType(newType)

		return pos

	def updateLexemeOffset(self):
		# just update not temporal lexemes
		# this will be called when parsing is executing
		self.currentOffset = 0
		for entry in self.st:
			if not entry.isTemporal():
				entry.setOffset(self.currentOffset)
				self.currentOffset += entry.getType().getSize()

	def updateOffset(self):
		# update all lexemes even temporals
		# this will be called when symbol table is finished
		self.currentOffset = 0
		for entry in self.st:
			if entry.getType().getTypeName() == "function":
				entry.setOffset(None)
			else:
				entry.setOffset(self.currentOffset)
				self.currentOffset += entry.getType().getSize()

			
	def setAttribute(self,lexeme):
		pos = self.getPos(lexeme)

		if (pos == None):
			print "%s no es un atributo de la clase:%s"%(lexeme,self.name)
			return None
		else:
		 	self.st[pos].setAttibute()

		return pos
		
	def getPos(self,lexeme):
		for  i in range(len(self.st)):
			if (self.st[i].getLexeme() == lexeme):
				return i
		return None

	def getType(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		return self.st[pos].getType()

		
	def getOffset(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		return self.st[pos].getOffset() 

	def getSymbolTableOffset():
		return self.currentOffset

	def getPost(self,lexeme):
		return lexeme

	def printAll(self):
		for i in range(len(self.st)):
			print "\t\t%i ==>: %s"%(i,self.st[i].showEntry())

	def getCopy(self): # just for object
		tmpName="TempObjectName_"+str(time())
		stCp = SymbolTable(tmpName)
		currentOffset = 0

		for entry in self.st:
			currentOffset = entry.getOffset()
			if (entry.isAttribute()):
				stCp.insert(entry.getLexeme(),entry.getType())
				stCp.setAttribute(entry.getLexeme())
			if(entry.isParam()):
				stCp.insert(entry.getLexeme(),entry.getType())
				stCp.setParam(entry.getLexeme())

		stCp.finish()
		return stCp


	def getParamsTypes(self):
		paramsTypes = []

		for entry in self.st:
			if entry.isParam():
				paramsTypes.append(entry.getType())
		return paramsTypes

	def getParams(self):
		params = []
		for entry in self.st:
			if entry.isParam():
				paramsTypes.append(entry.getLexeme())

		return params


	def setParamsTypes(self,paramsTypes):
		params = self.getParams()

		if (len(params) != len(paramsTypes)):
			raise StException("Error al indicar el tipo de los parametros de la funcion")

		for i in range(len(paramsTypes)):
			self.setType(params[i],paramsTypes[i])

	def paramsTypesStr(self):
		types = ""
		for param in self.getParamsTypes():
			types += " %s"%param.getTypeName()
		return types	


	def addParamType(self,lexeme,paramType):
		try:
			if (isinstance(paramType,Type)):
				self.setType(lexeme,paramType)
		except:
			print "Cannot add param %s because is not subclass of type"%paramType
	def setParam(self,lexeme):
		pos = self.getPos(lexeme)
		self.st[pos].setParam()

	def setAttribute(self,lexeme):
		pos = self.getPos(lexeme)
		self.st[pos].setAttribute()

	def setLocation(self,lexeme,location):

		#print "SET LOCATION:",lexeme
		attRe = re.compile("(.+)(\.)(.*)")
		attReRes = attRe.search(lexeme)


		if attReRes != None:
			#attributes in ST are stored as: this.attribute
			lexeme = "this."+attReRes.group(3)

		pos = self.getPos(lexeme)

		if pos == None:
			return pos

		self.st[pos].setLocation(location)

		return location

	def getLocation(self,lexeme):

		attRe = re.compile("(.+)(\.)(.*)")
		attReRes = attRe.search(lexeme)

		if attReRes != None:
			#attributes in ST are stored as: this.attribute
			lexeme = "this."+attReRes.group(3)

		pos = self.getPos(lexeme)

		if pos == None:
			return pos

		return self.st[pos].getLocation()

	def checkParams(self,paramsTypes):
		return self.checkTypes("param",paramsTypes)

	def getParamsLength(self):
		return len(self.getParamsTypes())

	def getParamsSize(self):
		paramsTypes = self.getParamsTypes()
		paramsSize = 0

		for paramType in paramsTypes:
			paramsSize += paramType.getSize()
		
		return paramsSize
			

	def checkTypes(self,paramsList):
		res = True
		
		typesList = self.getParamsTypes()

		if (len(typesList) != len(paramsList)):
			res = False
		else:
			for i in range(len(typesList)):
				#print typesList[i]
				#print paramsList[i]['type']
				if (typesList[i].getTypeName() != paramsList[i]['type'].getTypeName()): #TODO add method checkCompatibleTypes(type1,type2)
					res = False
					break
		return res

	def function2Object(self):
## TODO add params?
		return ObjectType(None)

	def setTemporal(self,temporal):
		pos = self.getPos(temporal)
		self.st[pos].setTemporal()

	def isTemporal(self,temporal):
		pos = self.getPos(temporal)
		self.st[pos].isTemporal()

	def isAlive(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos

		return self.st[pos].isAlive()
		
	def setNoLive(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		self.st[pos].setNoLive()
		return True

	def setLive(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		self.st[pos].setLive()
		return True

	def setNextUse(self,lexeme,index):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		self.st[pos].setNextUse(index)
		return True

	def setNoNextUse(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		self.st[pos].setNoNextUse()
		return True

	def getNextUse(self,lexeme):
		pos = self.getPos(lexeme)
		if pos == None:
			return pos
		return self.st[pos].getNextUse()
	
	def finish(self):
		temporals = []
		vlocals = []
		params = []

		# first of all, take out all temporals
		for stEntry in self.st:
			#print stEntry
			if stEntry.isTemporal():
				temporals.append(stEntry)

			elif stEntry.isParam():
				params.append(stEntry)
			else:
				vlocals.append(stEntry)

		params = params[::-1]


		self.st = params
		self.updateOffset()

		self.st += vlocals
		#
		self.updateOffset()
		# Note that updateOffset() is called but because all temporals have been removed previously
		#current offset just without temporal lexemes
		
		#temporals are appended after the last lexeme

		self.st += temporals

		self.updateOffset()

		self.finished = True


	def getObjectSize(self):
		size = 0
		for entry in self.st:
			if not isinstance(entry.getType,FunctionType):
				size += entry.getType().getSize()
		return size
